package com.zhangh.summer.admin.support.prometheus.thread;

import com.zhangh.summer.admin.support.prometheus.common.MetricsTool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @description: 线程收集器
 * @author: zhanghui  zhanghlucky@163.com
 * @create: 2020-06-29 16:09
 **/
@Component
@Slf4j
public class ThreadCollector {
    private static final String system_thread_info= "system_thread_info";
    private  ThreadGroup topGroup;

    @PostConstruct
    public void init(){
        MetricsTool.gauge().register(system_thread_info,"系统线程信息",
                "thread_id",
                "thread_name",
                "thread_name_pattern",
                "thread_state",
                "thread_class_name",
                "thread_group_name",
                "is_alive",
                "is_daemon",
                "is_interrupted",
                "priority");

        ThreadGroup group = Thread.currentThread().getThreadGroup();
        topGroup = group;
        while (group != null) {
            topGroup = group;
            group = group.getParent();
        }

    }

    public void execute(){
        try {
            //refresh();
        }catch (Exception e){
            log.warn("thread collect error:{}",e.getMessage(),e);
        }
    }

    private void refresh() {
        MetricsTool.gauge().get(system_thread_info).clear();
        Set<ThreadBO> threadBOSet=new HashSet<>();
        initSystemThreads(topGroup,threadBOSet);
        //计算name pattern
        List<ThreadBO> threadBOList= new ArrayList<>(threadBOSet);
        List<List<ThreadBO>>  threadTeamList=new ArrayList<>();
        List<Integer> delIndexList=new ArrayList<>();

        for (int i = 0; i < threadBOList.size(); i++) {
            if (!isInIndex(i,delIndexList)) {

                ThreadBO boi = threadBOList.get(i);
                List<ThreadBO> groupList = new ArrayList<>();
                groupList.add(boi);

                for (int j = (i + 1); j < threadBOList.size(); j++) {
                    if (!isInIndex(j,delIndexList)) {
                        ThreadBO boj = threadBOList.get(j);
                        int dis = stringDistance(boi.getName(), boj.getName());
                        if (dis <= 5) {//可认为相同
                            groupList.add(boj);
                            delIndexList.add(j);
                        }
                    }
                }
                threadTeamList.add(groupList);
            }
        }
        List<ThreadBO> resultList=new ArrayList<>();
        for (List<ThreadBO> threadBOS : threadTeamList) {
            String nameTeam=threadBOS.get(0).getName();
            if (threadBOS.size()>=2){
                String name1=threadBOS.get(0).getName();
                String name2=threadBOS.get(1).getName();
                if (!name1.equals(name2)){
                    char [] nameArray1=name1.toCharArray();
                    char [] nameArray2=name2.toCharArray();
                    int index=0;
                    for (int i = 0; i < nameArray1.length; i++) {
                        if(nameArray1[i]!=nameArray2[i]){
                            index=i-1;
                            break;
                        }
                    }
                    index=reSetIndex(nameArray1,index)+1;
                    nameTeam=name1.substring(0,index);
                }
            }
            for (ThreadBO bo : threadBOS) {
                bo.setNamePattern(nameTeam);
                resultList.add(bo);
            }
        }
        //--计算name pattern
        for (ThreadBO threadBO: resultList) {
            if (null!=threadBO) {
                MetricsTool.gauge().get(system_thread_info).
                        labels(String.valueOf(threadBO.getId()),
                                threadBO.getName(),
                                threadBO.getNamePattern(),
                                threadBO.getStateName(),
                                threadBO.getClassName(),
                                threadBO.getThreadGroupName(),
                                threadBO.getIsAlive(),
                                threadBO.getIsDaemon(),
                                threadBO.getIsInterrupted(),
                                threadBO.getPriority()).
                        set((double) 1);
            }
        }
    }

    private void initSystemThreads(ThreadGroup topGroup ,Set<ThreadBO> threadBOSet){
        int threadNum = topGroup.activeCount()*2;
        Thread[] threadArray = new Thread[threadNum];
        topGroup.enumerate(threadArray);
        for (int i = 0; i < threadNum; i++) {
            Thread thread = threadArray[i];
            if (null!=thread) {
                ThreadBO threadBO=new ThreadBO();
                threadBO.setId(String.valueOf(thread.getId()));
                threadBO.setName(thread.getName());
                threadBO.setStateName(thread.getState().name());
                threadBO.setClassName(thread.getClass().getCanonicalName()!=null?thread.getClass().getCanonicalName():thread.getClass().getName());
                threadBO.setThreadGroupName(thread.getThreadGroup().getName());
                threadBO.setIsAlive(String.valueOf(thread.isAlive()));
                threadBO.setIsDaemon(String.valueOf(thread.isDaemon()));
                threadBO.setIsInterrupted(String.valueOf(thread.isInterrupted()));
                threadBO.setPriority(String.valueOf(thread.getPriority()));
                threadBOSet.add(threadBO);
            }
        }
        int groupNum=topGroup.activeGroupCount()*2;
        ThreadGroup[] groupArray= new ThreadGroup[groupNum];
        topGroup.enumerate(groupArray);
        for (ThreadGroup group:groupArray) {
            if (null!=group) {
                initSystemThreads(group, threadBOSet);
            }
        }

    }


    private  int  reSetIndex(char [] nameArray1,int index){
        int reIndex=index;
        if (index<nameArray1.length && index>=0){
           // System.out.println(nameArray1.length+" "+index);
            char indexChar=nameArray1[index];
            if (Character.isDigit(indexChar)|| indexChar=='-'|| indexChar==' '){
                reIndex=reSetIndex(nameArray1,index-1);
            }
        }
        return reIndex;
    }

    private  boolean isInIndex(int index ,List<Integer> delIndexList){
        for (Integer idx : delIndexList) {
            if (index==idx.intValue()){
                return true;
            }
        }
        return false;
    }

    /**
     * 计算文本相似度 （动态规划）
     * @param source
     * @param target
     * @return
     */
    private   int stringDistance(String source, String target) {
        //长度大于15，只要前15位相同，我们认为是一个
        if (source.length()>15&&target.length()>15){
            if (source.substring(0,15).equals(target.substring(0,15))){
                return 0;
            }
        }

        char[] sources = source.toCharArray();
        char[] targets = target.toCharArray();
        int sourceLen = sources.length;
        int targetLen = targets.length;
        int[][] d = new int[sourceLen + 1][targetLen + 1];
        for (int i = 0; i <= sourceLen; i++) {
            d[i][0] = i;
        }
        for (int i = 0; i <= targetLen; i++) {
            d[0][i] = i;
        }

        for (int i = 1; i <= sourceLen; i++) {
            for (int j = 1; j <= targetLen; j++) {
                if (sources[i - 1] == targets[j - 1]) {
                    d[i][j] = d[i - 1][j - 1];
                } else {
                    int insert = d[i][j - 1] + 1;
                    int delete = d[i - 1][j] + 1;
                    int replace = d[i - 1][j - 1] + 1;
                    d[i][j] = Math.min(insert, delete) > Math.min(delete, replace) ? Math.min(delete, replace) :
                            Math.min(insert, delete);
                }
            }
        }
        return d[sourceLen][targetLen];
    }


}
